#! /usr/bin/env bash

VERBOSE=0
REDIS_CLI=redis-cli
TMP_FOLDER=/tmp
PREV_INFO_FILE=$TMP_FOLDER/redis_stat.pinfo
DEFAULT_INDEX="used_cpu_user,used_cpu_sys,connected_clients,blocked_clients,\
used_memory,used_memory_rss,keys,total_commands_processed_per_second,\
expired_keys_per_second,evicted_keys_per_second,keyspace_hit_ratio_per_second,\
keyspace_hits_per_second,keyspace_misses_per_second,aof_current_size"
MORE_INDEX="mem_fragmentation_ratio,total_commands_processed,expired_keys,\
evicted_keys,keyspace_hits,keyspace_misses,aof_base_size"

function collect {
	INFOS=$(echo "INFO" | $1/$REDIS_CLI -h $2 -p $3 2>/dev/null)
	if [[ -z "$INFOS" ]] ; then
		printf "无法连接位于$2:$3上的Redis服务器\n";
		exit 3;
	fi
	echo -e "$INFOS" | awk '
	BEGIN {
		FS="\n";

		len = split("'"$DEFAULT_INDEX"'", indexes, ",");
		more_length = 0;
		if("'"$VERBOSE"'" == 1) {
			more_length = split("'"$MORE_INDEX"'", more_indexes, ",");
		}

		jx = len;
		for(ix = 1; ix <= more_length; ++ix) {
			indexes[len + ix] = more_indexes[ix];
			++jx;
		}
		len = jx;
		load_prev_info();

		if(prev_info != "") {
			prev_len = split(prev_info, prev_list, ",");
			for(ix = 1; ix <= prev_len; ++ix) {
				split(prev_list[ix], tmp_list, ":");
				prev_hash[tmp_list[1]] = tmp_list[2];
			}
		}
	}

	function load_prev_info() {
		if(!system("test -f '$PREV_INFO_FILE'")) {
			"cat '$PREV_INFO_FILE'" | getline prev_info;
			close("cat '$PREV_INFO_FILE'");
		}
	}

	function get_per_seconds_data(key, second) {
		if(second <= 0) {
			return 0;
		} else {
			return ((hash[key] - prev_hash[key]) / second);
		}
	}

	function ratio(x, y) {
		percent = 0;
		if (x > 0 || y > 0) {
			percent = (x * 1.0 / (x + y) * 100);
		}

		return sprintf("%.2f%%", percent);
	}

	function humanize_number(num) {
		if(num == 0) {
			return 0;
		}

		return (num * 1.0 / 1024 / 1024);
	}

	{
		if( $0 ~ /^#|^[[:space:]]/) {
			next;
		}
		split($0, key_value, ":");
		hash[key_value[1]] = key_value[2];
	}

	END {
		"date +%s" | getline now;
		close("date +%s");
		dur = length(prev_hash) == 0 ? 0 : now - prev_hash["at"];

		for(ix = 1; ix <= len; ++ix) {
			if(indexes[ix] == "evicted_keys_per_second") {
				EVICTED_KEYS_PER_SECOND = get_per_seconds_data("evicted_keys", dur);
			}
			else if(indexes[ix] == "used_cpu_user") {
				USED_CPU_USER = hash["used_cpu_user"];
			}
			else if(indexes[ix] == "used_cpu_sys") {
				USED_CPU_SYS = hash["used_cpu_sys"];
			}
			else if(indexes[ix] == "expired_keys_per_second") {
				EXPIRED_KEYS_PER_SECOND = get_per_seconds_data("expired_keys", dur);
			}
			else if(indexes[ix] == "keyspace_hits_per_second") {
				KEYSPACE_HITS_PER_SECOND = get_per_seconds_data("keyspace_hits", dur);
			}
			else if(indexes[ix] == "keyspace_misses_per_second") {
				KEYSPACE_MISSES_PER_SECOND = get_per_seconds_data("keyspace_misses", dur);
			}
			else if(indexes[ix] == "total_commands_processed_per_second") {
				TOTAL_COMMANDS_PROCESSED_PER_SECOND = get_per_seconds_data("total_commands_processed", dur);
			}
			else if(indexes[ix] == "keyspace_hit_ratio") {
				hits = hash["keyspace_hits"];
				misses = hash["keyspace_misses"];
				KEYSPACE_HIT_RATIO = ratio(hits, misses);
			}
			else if(indexes[ix] == "keyspace_hit_ratio_per_second") {
				if(length(prev_hash) != 0) {
					hits_per_second = hash["keyspace_hits"] - prev_hash["keyspace_hits"];
					misses_per_second = hash["keyspace_misses"] - prev_hash["keyspace_misses"];
				}
				KEYSPACE_HIT_RATIO_PER_SECOND = ratio(hits_per_second, misses_per_second);
			}
			else if(indexes[ix] == "keys") {
				KEYS = 0;
				for(key in hash) {
					if(key ~ /^db[[:digit:]]+/) {
						split(hash[key], values, ",");
						split(values[1], part_key, "=");
						KEYS += part_key[2];
					}
				}
			}
		}

		printf "at:%d,used_cpu_user:%.2f,used_cpu_sys:%.2f,evicted_keys:%d,expired_keys:%d,keyspace_hits:%d, keyspace_misses:%d,total_commands_processed:%d\n", 
		now, hash["used_cpu_user"], hash["used_cpu_sys"], hash["evicted_keys"], hash["expired_keys"], 
		hash["keyspace_hits"], hash["keyspace_misses"], hash["total_commands_processed"] > "'"$PREV_INFO_FILE"'";

		printf "us=%.2f sy=%.2f cl=%d bcl=%d mem=%s rss=%s keys=%d cmdps=%.2f expps=%.2f evtps=%.2f hitpps=%s hitps=%.2f misps=%.2f",
		USED_CPU_USER, USED_CPU_SYS, hash["connected_clients"], hash["blocked_clients"], 
		humanize_number(hash["used_memory"]), humanize_number(hash["used_memory_rss"]), KEYS,
		TOTAL_COMMANDS_PROCESSED_PER_SECOND, EXPIRED_KEYS_PER_SECOND, EVICTED_KEYS_PER_SECOND, KEYSPACE_HIT_RATIO_PER_SECOND,
		KEYSPACE_HITS_PER_SECOND, KEYSPACE_MISSES_PER_SECOND;

		if("'"$VERBOSE"'" == 1) {
			printf " frag=%.2f cmd=%d exp=%d evt=%d hit=%d mis=%d", hash["mem_fragmentation_ratio"],
			hash["total_commands_processed"], hash["expired_keys"], hash["evicted_keys"], hash["keyspace_hits"],
			hash["keyspace_misses"];
		}

		if(hash["aof_enabled"] != 0) {
			printf " aofcs=%s aofbs=%s", humanize_number(hash["aof_current_size"]), humanize_number(hash["aof_base_size"])
		}
		printf "\n";
	}
	'
}
    

function usage {
	printf "\nUsage redis-stat:\n"
    printf "\t<-f redis-cli可执行程序的路径>\n"
    printf "\t[-H 设置Redis服务器的主机IP地址,默认为127.0.0.1]\n"
    printf "\t[-p 设置连接Redis服务器的端口,默认为6379]\n"
    printf "\t[-v 设置redis-stat的输出模式]\n"
    printf "\t-h 打印帮助信息\n"
}

while getopts :f:H:p:hv arg ; do
	case $arg in
		f) BINFOLDER=$OPTARG;;
		H) HOST=$OPTARG;;
		p) PORT=$OPTARG;;
        v) VERBOSE=1;;
		h) usage
		   exit 0;;
		?) printf "无效的参数!\n"
           usage
           exit 3;;
    esac
done

if [[ ${HOST} == "" ]] ; then
	HOST=127.0.0.1
fi

if [[ ${PORT} == "" ]] ; then
	PORT=6379
fi
PREV_INFO_FILE=$PREV_INFO_FILE-$HOST.$PORT		#aim to deal with multi-instance in one server

if [[ ${BINFOLDER} == "" ]] ; then
	printf "${BINFOLDER}路径下不存在可执行的redis-cli程序.\n"
    usage
    exit 3
fi

collect $BINFOLDER $HOST $PORT
